extern crate pkg_config;
use std::env;
use std::process::Command;
use std::process::exit;

fn build_emodp() {
    let topdir = match env::current_dir() {
        Ok(path) => path,
        Err(_) => {
            eprintln!("Failed to get current directory.");
            exit(1);
        }
    };

    let pkg_config_path = format!("{}/3rd/lib/pkgconfig", topdir.display());
    env::set_var("PKG_CONFIG_PATH", &pkg_config_path);

    let cargo_target = match env::var("CARGO_TARGET") {
        Ok(target) => target,
        Err(_) => String::new(),
    };

    if std::path::Path::new(&format!("{}/3rd/lib/pkgconfig/libemodp.pc", topdir.to_string_lossy())).exists()
    {
        return;
    }

    let mut cross = String::new();

    if cargo_target.starts_with("aarch64") {
        cross = "aarch64-linux-gnu".to_string();
    }

    println!("CROSS={}", cross);

    if !topdir.join("3rd/libconfig").exists() {
        let _ = Command::new("git")
            .args(&["clone", "https://github.com/hyperrealm/libconfig.git", "3rd/libconfig"])
            .current_dir(&topdir)
            .status();

        if !topdir.join("3rd/libconfig").exists() {
            eprintln!("Failed to clone libconfig repository.");
            exit(1);
        }
    }

    let _ = Command::new("sh")
        .args(&[
            "-c",
            &format!(
                "cd {}/3rd/libconfig && \
                 autoreconf -fvi && \
                 mkdir build && cd build && \
                 ../configure --prefix={} --host={} --disable-cxx --disable-doc && \
                 make && \
                 make install",
                topdir.display(),
                topdir.join("3rd").display(),
                cross
            ),
        ])
        .status();

    //==============================> build odp
    if !std::path::Path::new(&format!("{}/3rd/lib/pkgconfig/libodp-linux.pc", topdir.to_string_lossy())).exists()
    {
        if !topdir.join("3rd/odp").exists() {
            let _ = Command::new("git")
                .args(&["clone", "https://github.com/OpenDataPlane/odp.git", "3rd/odp"])
                .current_dir(&topdir)
                .status();

            if !topdir.join("3rd/odp").exists() {
                eprintln!("Failed to clone odp repository.");
                exit(1);
            }
        }

        let _ = Command::new("sh")
            .args(&[
                "-c",
                &format!(
                    "cd {}/3rd/odp && \
                    autoupdate && \
                    ./bootstrap && \
                    mkdir build && cd build && ../configure --prefix={} --host={} --without-openssl && \
                    make && \
                    make install",
                    topdir.display(),
                    topdir.join("3rd").display(),
                    cross
                ),
            ])
            .status();
    }


    //==============================> build em-odp
    if !topdir.join("3rd/em-odp").exists() {
        let _ = Command::new("git")
            .args(&["clone", "https://github.com/openeventmachine/em-odp.git", "3rd/em-odp"])
            .current_dir(&topdir)
            .status();

        if !topdir.join("3rd/em-odp").exists() {
            eprintln!("Failed to clone em-odp repository.");
            exit(1);
        }
    }

    // Apply patch if emodp.patch exists
    let patch_file = topdir.join("3rd/emodp.patch");
    let patch_done_file = topdir.join("3rd/patch_done");

    if patch_file.exists() && !patch_done_file.exists() {
        let apply_patch_result = std::process::Command::new("git")
            .args(&["am", &format!("{}/emodp.patch", topdir.to_string_lossy())])
            .current_dir(&format!("{}/3rd/em-odp", topdir.to_string_lossy()))
            .status();

        match apply_patch_result {
            Ok(status) => {
                if status.success() {
                    match std::fs::File::create(&patch_done_file) {
                        Ok(_) => println!("emodp patch done"),
                        Err(e) => eprintln!("Failed to create patch_done file: {:?}", e),
                    }
                } else {
                    eprintln!("Failed to apply emodp patch (git am exited with an error)");
                }
            }
            Err(e) => eprintln!("Failed to execute git am: {:?}", e),
        }
    }

    let _ = Command::new("sh")
        .args(&[
            "-c",
            &format!(
                "cd {}/3rd/em-odp && \
                autoupdate && \
                ./bootstrap && \
                mkdir build && cd build && ../configure --prefix={} --host={} --without-openssl && \
                make && \
                make install",
                topdir.display(),
                topdir.join("3rd").display(),
                cross
            ),
        ])
        .status();

}


fn build() {
    let out_dir = env::var("OUT_DIR").unwrap();

    println!("cargo:rustc-link-search=native={}", out_dir);
    println!("cargo:rustc-link-lib=static=cpp-cmake");

    if cfg!(windows) {
        cc::Build::new()
            .cpp_link_stdlib("stdc++")
            .file("example/main.c")
            .file("example/lib/hello_c.c")
            .file("example/lib/hello_cpp.cpp")
            .file("example/windows/win_main.cpp")
            .compile("libcpp-cmake.a");
        println!("cargo:rustc-link-lib=dylib=msvcrt");
        println!("cargo:rustc-link-lib=user32");//link to the MessageBox
    } else {
        cc::Build::new()
            .cpp_link_stdlib("stdc++")
            .file("example/main.c")
            .file("example/lib/hello_c.c")
            .file("example/lib/hello_cpp.cpp")
            .compile("libcpp-cmake.a");
        println!("cargo:rustc-link-lib=dylib=stdc++");
    }
}



fn main() {
    let topdir = match env::current_dir() {
        Ok(path) => path,
        Err(_) => {
            eprintln!("Failed to get current directory.");
            exit(1);
        }
    };

    let pkg_config_path = format!("{}/3rd/lib/pkgconfig", topdir.display());
    env::set_var("PKG_CONFIG_PATH", &pkg_config_path);

    build();
    build_emodp();

    if let Ok(lib) = pkg_config::Config::new().print_system_libs(false).probe("libemodp") {
        println!("Emodp library found at:");
        for lib_path in lib.link_paths.iter() {
            println!("{}", lib_path.to_string_lossy());
        }

        println!("cargo:rustc-link-lib=emodp");

        //println!("cargo:rustc-link-lib={}", lib.libs.iter().map(|s| format!("{}", s)).collect::<Vec<_>>().join(" "));
        //println!("cargo:{}", lib.include_paths.iter().map(|s| format!("-I{}", s.to_string_lossy())).collect::<Vec<_>>().join(" "));

    } else {
        eprintln!("Emodp library not found.");
        exit(1);
    }
}